﻿using System;
using System.Globalization;
using System.Linq;
using GammaJul.ReSharper.GammaXaml.Psi.Xaml;
using JetBrains.Annotations;
using JetBrains.Application;
using JetBrains.Application.Progress;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.LiveTemplates.Hotspots;
using JetBrains.ReSharper.Feature.Services.LiveTemplates.LiveTemplates;
using JetBrains.ReSharper.Feature.Services.Xaml.Bulbs;
using JetBrains.ReSharper.LiveTemplates;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Xaml.Impl;
using JetBrains.ReSharper.Psi.Xaml.Tree;
using JetBrains.ReSharper.Psi.Xml.Parsing;
using JetBrains.ReSharper.Psi.Xml.Tree;
using JetBrains.TextControl;
using JetBrains.Util;

namespace GammaJul.ReSharper.GammaXaml.ContextActions.Xaml {

	/// <summary>
	/// Context action that adds a row or column definition.
	/// </summary>
	public class AddGridDefinitionItem : GridDefinitionItemBase {
		private readonly BeforeOrAfter _beforeOrAfter;

		/// <summary>
		/// Gets whether the definition will be added before or after the current definition.
		/// </summary>
		public BeforeOrAfter BeforeOrAfter {
			get { return _beforeOrAfter; }
		}

		/// <summary>
		/// Gets the popup menu item text.
		/// </summary>
		public override string Text {
			get { return "Add " + DefinitionType.ToString() + "Definition " + (BeforeOrAfter == BeforeOrAfter.After ? "after" : "before"); }
		}

		protected override Action<ITextControl> ExecutePsiTransaction(ISolution solution, IProgressIndicator progress) {
			DefinitionInfo defInfo = GetDefinitionInfoAtCaret();
			if (defInfo == null)
				return null;

			int affectedIndex = defInfo.DefinitionsSetter.InnerTags.IndexOf(defInfo.Definition);
			if (BeforeOrAfter == BeforeOrAfter.After)
				++affectedIndex;

			// updates Grid.Row/Grid.Column on the children of the grid
			UpdateGridChildrenDefinitionValue(
				defInfo.Grid,
				defInfo.GridType,
				def => def >= affectedIndex ? def + 1 : def,
				(def, span) => span);

			// adds a new definition before or after the current one
			string newDefString = String.Format(CultureInfo.InvariantCulture, "<{0}Definition {1}=\"Auto\"/>", DefinitionType, DefinitionType.GetSizeString());
			IXmlTag newDef = XamlElementFactory.GetElementFactory(defInfo.Definition).CreateTag(newDefString, Provider.PsiServices, Provider.PsiModule, true);
			newDef = BeforeOrAfter == BeforeOrAfter.After
				? defInfo.DefinitionsSetter.AddTagAfter(newDef, defInfo.Definition)
				: defInfo.DefinitionsSetter.AddTagBefore(newDef, defInfo.Definition);

			Provider.PsiServices.MarkAsDirty(Provider.SourceFile);

			// gets the "Auto" range in the newly created definition so that we can create a hotspot session onto it
			var value = ((IPropertyAttributeValue) newDef.GetAttributes().First().Value);
			Assertion.Assert(value != null, "value != null");
			TextRange autoRange = value.GetTextToken().GetDocumentRange().TextRange;

			// after the transaction, creates an hotspot session on "Auto" with "Auto" and "*" (which is what R# basic completion provides)
			return textControl => Shell.Instance
			    .GetComponent<LiveTemplatesManager>()
			    .CreateHotspotSessionAtopExistingText(
			        Provider.Solution,
			        new TextRange(autoRange.EndOffset),
			        textControl,
			        LiveTemplatesManager.EscapeAction.LeaveTextAndCaret,
			        new HotspotInfo(new TemplateField("Auto", new NameSuggestionsExpression(new[] { "Auto", "*" }), 0), autoRange))
			    .Execute();
		}

		public AddGridDefinitionItem([NotNull] XamlContextActionDataProvider provider, GridDefinitionType definitionType, BeforeOrAfter beforeOrAfter)
			: base(provider, definitionType) {
			_beforeOrAfter = beforeOrAfter;
		}
	}

}